¿Cómo y dónde se producen los accidentes en Madrid?

Autores/as

Gema Fernández-Avilés (Gema.FAviles@uclm.es)

Isidro Hidalgo (Isidro.Hidalgo@uclm.es)

Fecha de publicación

20 de enero de 2025

Nota

Los datos que se utilizan en esta historia están disponibles en el paquete CDR que puede instalarse con el siguiente comando (se comprueba si no lo está):

Instalación y/o carga del paquete CDR
if (!require(CDR)){
  if (!require(remotes)) {install.packages("remotes")}
  remotes::install_github("cdr-book/CDR")
  }

Accidentes de tráfico en la Ciudad de Madrid registrados por Policía Municipal con víctimas y/o daños al patrimonio en el 2020. Los datos se han descargado del Portal de datos abiertos del Ayuntamiento de Madrid.)

1 Cómo y dónde se producen los accidentes en Madrid

Esta mañana estaba tan tranquilo en mi oficina (trabajo en el distrito de Chamartín para el Ayuntamiento) y me sueltan de repente: “el jefe quiere verte”. Lo primero que he pensado es que me echaban, como soy un poco “cansa almas”… Pero no, entro en el despacho del jefe… ¡Y allí está Almeida! Que ha oído que he hecho un curso de visualización en la UCLM y me he convertido en un crack de la visualización. Yo no sabía donde meterme, pero he mantenido perfectamente la compostura, como un profesional.

Así que ya me ha ido contando que está muy preocupado con los accidentes y quiere analizar el tema, para buscar soluciones. Que me agradecería mucho si encontramos dónde está el problema, para bajar el número y gravedad de los accidentes en la ciudad. El problema, me dice, es que solo me puede facilitar los datos del año 2020. Ya le he avisado de que es un año muy particular, por el estado de alarma, pero algo sacaremos…

2 Entender el contexto

Cómo definir el propósito y la audiencia de tu análisis

¿Qué le interesa al alcalde? Bajar la tasa de accidentes. Pero desde el punto de vista de la visualización, está claro que va a necesitar información georreferenciada, por lo que haremos mapas. Por otro lado, seguro que la dimensión es importante, porque no es lo mismo Madrid en agosto que un día de lluvia en calendario escolar…

En cualquier caso, lo primero es hacer un análisis exploratorio para ver si hay algo raro en los datos y analizar mínimamente las variables de interés. Y después, le vamos a preparar los gráficos necesarios para aportarle conocimiento sobre los accidentes en la ciudad.

3 Elegir una visualización adecuada

Selección de gráficos y visualizaciones que mejor representen tus datos.

3.1 Almeida quiere saber cómo y dónde se producen los accidentes en Madrid

Ejercicio 1 ¿Dónde se han producido los accidentes de tráfico en Madrid en el 2020?

primer mapa
data(accidentes2020_data)

ggplot(data = accidentes2020_data,
       aes(x = coordenada_x_utm, y = coordenada_y_utm)) + 
  geom_point() +
  coord_fixed()

3.2 El desorden es tu enemigo

Deja mucho que desear, ¿verdad?: no tenemos información, solo unos datos representados en un gráfico. Con muy poco, tirando de estética (reduciendo la dimensión de los puntos para que se vea el fondo) y alguna referencia geográfica (en forma de mapa de carreteras), veremos cómo se reduce la carga cognitiva:

primer mapa mejorado
accidentes2020_sf <- st_as_sf(accidentes2020_data,
  coords = c("coordenada_x_utm", "coordenada_y_utm"),
  crs = 25830 # proyección ETRS89/ UTM zone 30N. Área de uso: Europa  
  )  

madrid <- esp_get_munic(munic = "^Madrid$") |>
  st_transform(25830) 

# descarga imagen de un de mapa estático de las carreteras de Madrid
tile <- esp_getTiles(madrid, "IDErioja", zoommin = 2)  

ggplot() +
  geom_spatraster_rgb(data = tile) +
  geom_sf(data = accidentes2020_sf, 
    col = "blue", size = 0.05, alpha = 0.3) +
  coord_sf(expand = FALSE) +
  labs(title = "¿Dónde se producen los accidentes de tráfico en Madrid?")

¡Esto ya es otra cosa! Apreciamos, lógicamente, la concentración de accidentes en el centro de la ciudad y, especialmente, en las arterias principales.

3.3 El cuándo

Además, como hemos comentado, tenemos conocimiento a priori sobre la influencia de la fecha en el aumento de tráfico, por lo que probablemente encontremos alguna influencia de la dimensión temporal. Vamos a verlo…

Ejercicio 2 ¿Cuándo se han producido los accidentes?

primer gráfico del cuándo
ggplot(data = accidentes2020_data,
       aes(x = fecha, y = hora)) + 
  geom_point() +
  theme_minimal()

3.4 Eliminemos de nuevo el desorden

Vamos a separar la fecha y la hora, porque en ese gráfico no vemos casi nada…

gráfico del cuándo según el día
# Convertir la columna 'fecha' a formato Date
accidentes2020_data$fecha <- as.Date(accidentes2020_data$fecha, format = "%d/%m/%Y")

accidentes2020_fecha <- accidentes2020_data |>
  select(fecha) |>
  group_by(fecha) |>
  mutate(n = n()) |>
  unique()

ggplot(data = accidentes2020_fecha,
       aes(x = fecha, y = n)) + 
  geom_line() +
  theme_minimal()

Como ya le advertimos al alcalde, el año 2020 no es bueno para sacar conclusiones. Ya le hemos pedido que nos facilite datos más amplios para poder proporcionarle una mejor visualización del número de accidentes por fecha, pero mientras tanto, nos tendremos que arreglar con lo que tenemos…

gráfico del cuándo según la hora
# Convertir la columna 'hora' a formato Date
accidentes2020_data$hora_dia <- hour(as.POSIXct(accidentes2020_data$hora, format = "%H:%M:%S"))

accidentes2020_hora_dia <- accidentes2020_data |>
  select(hora_dia) |>
  group_by(hora_dia) |>
  mutate(n = n()) |>
  unique()

ggplot(data = accidentes2020_hora_dia,
       aes(x = hora_dia, y = n)) + 
  geom_line() +
  theme_minimal()

Es evidente que la hora punta a mediodía y en la salida del trabajo, a las 14 y las 19 horas del día son los 2 picos a vigilar. Pero este gráfico tiene un gran margen de mejora. Hala, a trabajar un poquito y a dejarlo “niquelao”, que ya sabéis muchas cosas, cracks:

Tu turno

Completa las partes del código señaladas por _____ o xxxxx para obtener el resultado propuesto.

Código
ggplot(data = accidentes2020_hora_dia,
       aes(x = hora_dia, y = n)) + 
  geom_xxxx(linewidth = 1, color = "orange") +
  labs(_____ = "Número de accidentes según la franja horaria del día",
       subtitle = "Las 14 y las 19 horas presentan el mayor número de accidentes",
       x = "Hora del día", y = "Número de accidentes en la franja horaria") +
  scale_y__________(labels = function(x) {
    format(x,
      big.mark = ".", decimal.mark = ",", scientific = FALSE
    )
  }) +
  theme________() +
  theme(axis.title.x = element_____(hjust = 0),
        axis.title.y = element_text(hjust = 1))

Ejercicio 3 ¿De qué tipo son los accidentes?

Código
accidentes2020_data <- accidentes2020_data |>
  mutate_if(is.character, as.factor)

accidentes2020_data |>
  count(tipo_accidente) |>
  mutate(porcentaje = 100 * n / sum(n))
                  tipo_accidente     n porcentaje
                          <fctr> <int>      <num>
 1:                      Alcance  7294 22.4936010
 2:           Atropello a animal    75  0.2312887
 3:          Atropello a persona  2127  6.5593487
 4:                        Caída  2118  6.5315940
 5: Choque contra obstáculo fijo  4667 14.3923274
 6:             Colisión frontal   899  2.7723810
 7:      Colisión fronto-lateral  8081 24.9205909
 8:             Colisión lateral  4386 13.5257656
 9:            Colisión múltiple  2231  6.8800691
10:                Despeñamiento     2  0.0061677
11:                         Otro   251  0.7740463
12:        Solo salida de la vía   151  0.4656613
13:                       Vuelco   145  0.4471582

3.5 El tipo de accidente

tipos de accidente
accidentes2020_data |>
  ggplot() +
  geom_bar(aes(y=tipo_accidente), fill = "orange", alpha = 0.6) +
  theme_bw()

¿Qué le pasa a este gráfico? ¿En que se puede mejorar?

Ejercicio 4 ¿Existe asociación entre el sexo y el tipo de persona (conductor, pasajero, peaton) que sufre el accidente?

Código
# accidentes2020_data_sin_na <- accidentes2020_data |>
#  na.omit()
  
balloonplot(table(accidentes2020_data$sexo , accidentes2020_data$tipo_persona))

Ejercicio 5 ¿Existe asociación entre el sexo y el tipo de accidente?

asociación sexo-tipo
accidentes2020_data |>
ggplot() +
  geom_mosaic(aes(x = product(tipo_accidente, sexo), 
                  fill=sexo)) 

Código
niveles <- levels(factor(accidentes2020_data$tipo_accidente))
etiquetas <- set_names(str_wrap(niveles, width = 20), 
                       niveles)
accidentes2020_data  |>
  ggplot(aes(sexo, fill = estado_meteorológico)) +
  facet_wrap(vars(tipo_accidente), 
             labeller = as_labeller(etiquetas)) +
  geom_bar() +
  labs(fill = "Estado Meteorológico") +
  theme(axis.text.x  = element_text(angle = 90)) +
  theme_minimal()

4 Centra la atención de tu audiencia

Código
# con la librería leflet podemos crear un mapa interactivo


utm_crs <- "+proj=utm +zone=30 +datum=WGS84 +units=m +no_defs"
accidentes_sf <- st_as_sf(accidentes2020_data, 
                          coords = c("coordenada_x_utm", "coordenada_y_utm"), crs = utm_crs)
accidentes_sf <- st_transform(accidentes_sf, crs = 4326)
accidentes2020_data$longitude <- st_coordinates(accidentes_sf)[, 1]
accidentes2020_data$latitude <- st_coordinates(accidentes_sf)[, 2]

leaflet(data = accidentes2020_data) %>%
  addTiles() %>%
  addCircleMarkers(lng = ~longitude, lat = ~latitude, 
                   radius = 2, color = "red", opacity = 0.5) %>%
  addProviderTiles(providers$Esri.WorldImagery)
Código
accidentes2020_sf <- st_transform(accidentes2020_sf, crs = 4326)
accidentes2020_data$longitude <- st_coordinates(accidentes2020_sf)[, 1]
accidentes2020_data$latitude <- st_coordinates(accidentes2020_sf)[, 2]

leaflet(data = accidentes2020_data) %>%
  addTiles() %>%
  addCircleMarkers(lng = ~longitude, lat = ~latitude, 
                   radius = 1, color = "red", opacity = 0.5)  
Código
#  addProviderTiles(providers$Esri.WorldImagery)

5 Piensa como un diseñador.

Código
leaflet(data = accidentes2020_data) %>%
  addTiles() %>%
  addCircleMarkers(lng = ~longitude, lat = ~latitude, 
                   radius = 2, color = "red", opacity = 0.5, clusterOptions = markerClusterOptions())
Código
# ahora haz un mapa de calor
leaflet(data = accidentes2020_data) %>%
  addTiles() %>%
  addHeatmap(lng = ~longitude, lat = ~latitude, blur = 20, max = 0.05)

Ejercicio 6 ¿Dónde se registraron los accidentes el día de la declaración del Estado de Alarma en Madrid?

Código
# Convertir la columna 'fecha' a formato Date
accidentes2020_data[, fecha := as.Date(fecha, format = "%d/%m/%Y")]

# Definir el rango de fechas del estado de alarma
fecha_inicio <- as.Date("2020-03-14")
fecha_fin <- as.Date("2020-03-14")

# Filtrar el dataset por el rango de fechas
estado_alarma_data <- accidentes2020_data[fecha >= fecha_inicio & fecha <= fecha_fin]

# Crear un mapa con los accidentes
leaflet(data = estado_alarma_data) %>%
  addTiles() %>%
  addCircleMarkers(lng = ~longitude, lat = ~latitude, 
                   radius = 3, color = "red", opacity = 0.5)